package com.polaris.live.utils.file

import android.content.Context
import android.os.Environment
import androidx.annotation.CheckResult
import androidx.annotation.IntDef
import com.blankj.utilcode.constant.MemoryConstants
import com.blankj.utilcode.util.SDCardUtils
import com.hjq.language.MultiLanguages
import com.polaris.live.App
import com.polaris.live.common.appContext
import com.polaris.live.common.constant.PagConst.LOAD_FILE_PAG
import com.polaris.live.common.manager.CoroutineScopeManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.nio.charset.StandardCharsets

/**
 * 文件相关
 *
 * @author Created by lucas on 2023/10/26 19:12
 * @since 1.0.0
 */
object FileUtils {

    /**
     * 创建文件夹
     */
    @CheckResult
    fun createDir(path: String): String {
        val file = File(path)
        if (!file.exists()) {
            file.mkdirs()
        }
        return path
    }

    /**
     * 获得缓存目录
     */
    @CheckResult
    fun getCacheDir(name: String? = null): String {
        val context = App.instance
        var path = if (SDCardUtils.isSDCardEnableByEnvironment()) {
            val df = context.externalCacheDir
            if (df != null) {
                df.path
            } else {
                context.cacheDir.path
            }
        } else {
            context.cacheDir.path
        }
        if (!name.isNullOrEmpty()) {
            path += File.separator + name
        }
        return createDir(path)
    }

    /**
     * 获得存储目录
     */
    @CheckResult
    fun getFilesDir(name: String? = null): String {
        val context = App.instance
        val path = if (SDCardUtils.isSDCardEnableByEnvironment()) {
            val file = context.getExternalFilesDir(name)
            if (file != null) {
                file.path
            } else {
                getFilesDirName(context, name)
            }
        } else {
            getFilesDirName(context, name)
        }
        return createDir(path)
    }

    private fun getFilesDirName(context: Context, name: String?): String {
        var path = context.filesDir.path
        if (!name.isNullOrEmpty()) {
            path += File.separator + name
        }
        return path
    }

    /**
     * 获取文件目录
     */
    @CheckResult
    fun getDir(name: String? = null): String {
        val context = App.instance
        var path = if (SDCardUtils.isSDCardEnableByEnvironment()) {
            Environment.getExternalStorageDirectory().path + File.separator + "psd"
        } else {
            context.cacheDir.absolutePath
        }
        if (!name.isNullOrEmpty()) {
            path += File.separator + name
        }
        return createDir(path)
    }

    /**
     * 获取原始文件路径
     */
    @CheckResult
    fun getSourceDir(name: String? = null): String {
        val context = App.instance
        var path = if (SDCardUtils.isSDCardEnableByEnvironment()) {
            Environment.getExternalStorageDirectory().path
        } else {
            context.cacheDir.absolutePath
        }
        if (!name.isNullOrEmpty()) {
            path += File.separator + name
        }
        return path
    }

    /**
     * 获得临时缓存目录
     */
    @CheckResult
    fun getTempCacheDir(): String {
        return getCacheDir("temp")
    }

    /**
     * 获得图片缓存目录
     */
    @CheckResult
    fun getImageCacheDir(): String {
        return getCacheDir("image")
    }

    /**
     * 获得下载缓存目录
     */
    @CheckResult
    fun getDownloadCacheDir(): String {
        return getCacheDir("downloadTemp")
    }

    /**
     * zego日志目录
     */
    @CheckResult
    fun getZegoLogCacheDir(): String {
        return getCacheDir("zego")
    }

    /**
     * 获得礼物目录
     */
    @CheckResult
    fun getPagFile(): String {
        return getFilesDir("pag")
    }

    /**
     * 获得音频目录
     */
    @CheckResult
    fun getAudioFile(): String {
        return getFilesDir("audio")
    }

    /**
     * 获得bubble存储目录
     */
    @CheckResult
    fun getBubbleFile(): String {
        return getFilesDir("bubble")
    }

    @CheckResult
    fun getAssetsToFile(): String {
        return getFilesDir("assetsPagFile")
    }

    /**
     * 获得美颜滤镜目录
     */
    @CheckResult
    fun getBeautyDir(): String {
        return getFilesDir("beauty")
    }

    /**
     * 获取系统相机目录
     */
    @CheckResult
    fun getSystemCamera(): String {
        val path =
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).absolutePath + "/Camera"
        return createDir(path)
    }

    /**
     * 复制Assets文件夹到本地目录，递归
     */
    suspend fun saveAssetsDir(assetDir: String, targetDir: String) {
        if (assetDir.isEmpty() || targetDir.isEmpty()) {
            return
        }

        val separator = File.separator
        // 获取assets目录assetDir下一级所有文件以及文件夹
        val fileNames = App.instance.resources.assets.list(assetDir)
        // 如果是文件夹(目录),则继续递归遍历
        if (fileNames?.isNotEmpty() == true) {
            val targetFile = File(targetDir)
            if (!targetFile.exists() && !targetFile.mkdirs()) {
                return
            }
            for (fileName in fileNames) {
                saveAssetsDir(assetDir + separator + fileName, targetDir + separator + fileName)
            }
        } else {
            saveAssetsFile(assetDir, targetDir)
        }
    }

    /**
     * 保存Assets文件
     *
     * @param name assets地址
     * @param path 本地地址
     */
    suspend fun saveAssetsFile(name: String, path: String) {
        saveAssetsFile(name, File(path))
    }

    /**
     * 保存Assets文件到本地目录
     *
     * @param name assets地址
     * @param file 文件
     */
    suspend fun saveAssetsFile(name: String, file: File) {
        withContext(Dispatchers.IO) {
            try {
                val buffer = App.instance.assets.open(name).use {
                    if (file.exists() && it.available().toLong() == file.length()) {
                        return@withContext
                    }

                    ByteArray(it.available()).apply { it.read(this) }
                }
                FileOutputStream(file).use { out -> out.write(buffer) }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 读取Assets中的文本
     *
     * @param name assets地址
     */
    suspend fun readAssetsText(name: String): String {
        return withContext(CoroutineScopeManager.ioDispatcher) {
            readAssetsTextWithCurrentThread(name)
        }
    }

    /**
     * 在当前线程读取
     */
    fun readAssetsTextWithCurrentThread(name: String): String {
        return App.instance.assets.open(name).use {
            val buffer = ByteArray(it.available())
            it.read(buffer)
            String(buffer, StandardCharsets.UTF_8)
        }
    }

    /**
     * 读取Assets文件
     */
    suspend fun readAssetsFile(name: String): ByteArray {
        return withContext(CoroutineScopeManager.ioDispatcher) {
            readAssetsFileWithCurrentThread(name)
        }
    }

    fun readAssetsFileWithCurrentThread(name: String): ByteArray {
        return App.instance.assets.open(name).use {
            ByteArray(it.available()).apply {
                it.read(this)
            }
        }
    }

    /**
     * 获得文件大小
     *
     * @param path 文件地址
     */
    fun getFileSize(path: String): Int {
        val file = File(path)
        if (!file.exists()) {
            return 0
        }
        return if (file.isDirectory) {
            getDirSize(file)
        } else {
            getFileSize(file)
        }
    }

    fun getFileSizeToString(file: File): String {
        val fileSize = file.length()
        val units = arrayOf("B", "KB", "MB", "GB", "TB")

        var size = fileSize.toDouble()
        var unitIndex = 0

        while (size > 1024 && unitIndex < units.size - 1) {
            size /= 1024
            unitIndex++
        }

        return "%.2f %s".format(size, units[unitIndex])
    }

    /**
     * 获得文件大小
     *
     * @param file 文件
     */
    fun getFileSize(file: File): Int {
        return file.length().toInt()
    }

    //获取文件夹大小
    private fun getDirSize(file: File): Int {
        var size = 0
        val files = file.listFiles() ?: return 0
        for (f in files) {
            size = if (f.isDirectory) {
                size + getDirSize(f)
            } else {
                size + getFileSize(f)
            }
        }
        return size
    }

    const val SIZE_BYTE = 0
    const val SIZE_KB = 1
    const val SIZE_MB = 2
    const val SIZE_GB = 3

    @IntDef(SIZE_BYTE, SIZE_KB, SIZE_MB, SIZE_GB)
    @Retention(AnnotationRetention.SOURCE)
    annotation class SizeType

    /**
     * 格式化大小
     *
     * @param size 大小
     */
    fun formatFileSize(size: Float): String {
        return formatFileSize(size, SIZE_BYTE)
    }

    /**
     * 格式化大小
     *
     * @param size    大小
     * @param oldType 旧类型
     */
    fun formatFileSize(size: Float, @SizeType oldType: Int): String {
        val byteSize = convertFileSize(size, oldType, SIZE_BYTE).toDouble()
        return if (byteSize < MemoryConstants.KB) {
            String.format(MultiLanguages.getSystemLanguage(appContext), "%.2fB", byteSize)
        } else if (byteSize < MemoryConstants.MB) {
            String.format(
                MultiLanguages.getSystemLanguage(appContext),
                "%.2fKB",
                byteSize / MemoryConstants.KB.toFloat()
            )
        } else if (byteSize < MemoryConstants.GB) {
            String.format(
                MultiLanguages.getSystemLanguage(appContext),
                "%.2fMB",
                byteSize / MemoryConstants.MB.toFloat()
            )
        } else {
            String.format(
                MultiLanguages.getSystemLanguage(appContext),
                "%.2fGB",
                byteSize / MemoryConstants.GB.toFloat()
            )
        }
    }

    /**
     * 转换大小单位，默认旧单位为[SizeType.SIZE_BYTE]
     *
     * @param size    大小
     * @param newType 新类型
     */
    fun convertFileSize(size: Float, @SizeType newType: Int): Float {
        return convertFileSize(size, SIZE_BYTE, newType)
    }

    /**
     * 转换大小单位
     *
     * @param size    大小
     * @param oldType 旧类型
     * @param newType 新类型
     */
    fun convertFileSize(size: Float, @SizeType oldType: Int, @SizeType newType: Int): Float {
        var size = size
        var poor = newType - oldType
        if (poor > 0) {
            for (i in 0 until poor) {
                size /= 1024.0.toFloat()
            }
        } else if (poor < 0) {
            poor = Math.abs(poor)
            for (i in 0 until poor) {
                size *= 1024.0.toFloat()
            }
        }
        return size
    }

    //获取从assets中保存到文件里的pag动画的位置
    fun getAssetsToFilePag(pagName: String) = getAssetsToFile() + "/${LOAD_FILE_PAG}/$pagName"

    //获取文件地址
    fun getFilePath(directory: String, path: String) = "$directory/$path"

}
